Skip to content

fix(electron): block untrusted window.open and navigations#74

Merged
ubugeeei merged 1 commit into
codex/vue-supportfrom
codex/prod-window-open-handler
Jun 2, 2026
Merged

fix(electron): block untrusted window.open and navigations#74
ubugeeei merged 1 commit into
codex/vue-supportfrom
codex/prod-window-open-handler

Conversation

@ubugeeei
Copy link
Copy Markdown
Collaborator

Summary

  • Add a hardenWindow helper that wires setWindowOpenHandler, will-navigate, and will-redirect on every BrowserWindow.
  • Untrusted window.open targets are delegated to shell.openExternal so they leave the app instead of being loaded with the preload bridge attached.
  • Untrusted top-level navigations are cancelled outright.

Closes #45 and #46.

Test plan

  • npm run typecheck
  • npm run lint
  • Manually verify Studio/Render still launch in dev mode (npm run dev).

🤖 Generated with Claude Code

Closes #45 and #46.

Add a hardenWindow helper that wires setWindowOpenHandler /
will-navigate / will-redirect on every BrowserWindow so that only
URLs accepted by assertTrustedUrl can load inside the app.
Untrusted window.open targets are delegated to shell.openExternal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b72719e67

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread electron/main.ts
Comment on lines +130 to +132
if (isTrustedUrl(url)) {
return { action: "allow" }
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject opaque-origin URLs in window-open trust check

This allow branch trusts any URL whose origin is "null", which includes schemes like data: and javascript: (not just your app files). Because isTrustedUrl() delegates to assertTrustedUrl(), a renderer can still open opaque-origin popup URLs inside Electron instead of being denied/externalized, which bypasses the new hardening goal for untrusted window.open targets.

Useful? React with 👍 / 👎.

Comment thread electron/main.ts
Comment on lines +129 to +132
win.webContents.setWindowOpenHandler(({ url }) => {
if (isTrustedUrl(url)) {
return { action: "allow" }
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Harden BrowserWindow instances created by allowed popups

Returning { action: "allow" } lets Electron create a new BrowserWindow, but this code never attaches hardenWindow() to that child window. As a result, any allowed popup (trusted initial URL) can later navigate/open untrusted URLs without the will-navigate / will-redirect / setWindowOpenHandler guards applied here, leaving a bypass path for the new protections.

Useful? React with 👍 / 👎.

Comment thread electron/main.ts
if (isTrustedUrl(url)) {
return { action: "allow" }
}
void shell.openExternal(url).catch(() => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allowlist protocols before calling shell.openExternal

This forwards every untrusted popup URL directly into shell.openExternal, including attacker-controlled custom protocols. Electron’s security guidance warns this can be abused to invoke arbitrary external handlers/apps, so a compromised renderer (e.g., XSS in any trusted page) can escalate impact from “blocked in-app popup” to launching host-level protocol handlers unless you explicitly restrict allowed schemes (typically https/http, optionally mailto).

Useful? React with 👍 / 👎.

Comment thread electron/main.ts
Comment on lines +130 to +132
if (isTrustedUrl(url)) {
return { action: "allow" }
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit file:// trust when allowing window.open targets

The trust predicate returns true for any file: URL, and this handler now uses that predicate to allow popup creation. That means a renderer can open arbitrary local files (e.g. file:///tmp/...) inside the app instead of being denied/externalized, which undermines the “untrusted window.open” hardening in this commit and expands the set of in-app destinations far beyond your packaged app content.

Useful? React with 👍 / 👎.

@ubugeeei ubugeeei merged commit ac79d57 into codex/vue-support Jun 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant